<?php

declare(strict_types=1);

namespace app\service\order;

use app\model\ShipmentCompany;
use InvalidArgumentException;
use think\Paginator;

class ShipmentCompanyService
{
    public function paginate(array $filters, int $page, int $pageSize): Paginator
    {
        $query = ShipmentCompany::order(['sort' => 'asc', 'id' => 'asc']);

        if (!empty($filters['keyword'])) {
            $keyword = trim((string) $filters['keyword']);
            $query->where(function ($subQuery) use ($keyword) {
                $subQuery->whereLike('name', '%' . $keyword . '%')
                    ->whereOr('code', 'like', '%' . $keyword . '%');
            });
        }

        if ($filters['status'] ?? '' !== '') {
            $query->where('status', (int) $filters['status']);
        }

        return $query->paginate([
            'list_rows' => $pageSize,
            'page' => $page,
        ]);
    }

    public function findById(int $id): ?ShipmentCompany
    {
        return ShipmentCompany::find($id);
    }

    public function create(array $payload): ShipmentCompany
    {
        $data = $this->extractPayload($payload, true);
        $this->assertCodeUnique($data['code'] ?? '', null);

        /** @var ShipmentCompany $company */
        $company = ShipmentCompany::create($data);

        return $this->findById((int) $company->id);
    }

    public function update(ShipmentCompany $company, array $payload): ShipmentCompany
    {
        $data = $this->extractPayload($payload, false);

        if (isset($data['code']) && $data['code'] !== $company->code) {
            $this->assertCodeUnique($data['code'], (int) $company->id);
        }

        $company->save($data);

        return $this->findById((int) $company->id);
    }

    public function delete(ShipmentCompany $company): bool
    {
        return (bool) $company->delete();
    }

    public function format(ShipmentCompany $company): array
    {
        return [
            'id' => (int) $company->id,
            'name' => $company->name,
            'code' => $company->code,
            'contact_phone' => $company->contact_phone,
            'contact_email' => $company->contact_email,
            'website' => $company->website,
            'tracking_url' => $company->tracking_url,
            'delivery_type' => $company->delivery_type,
            'support_cod' => (int) $company->support_cod,
            'api_type' => $company->api_type,
            'api_config' => $company->api_config ?? [],
            'required_fields' => $company->required_fields ?? [],
            'status' => (int) $company->status,
            'sort' => (int) $company->sort,
            'create_time' => $company->create_time,
            'update_time' => $company->update_time,
        ];
    }

    public function formatCollection(iterable $companies): array
    {
        $results = [];
        foreach ($companies as $company) {
            if ($company instanceof ShipmentCompany) {
                $results[] = $this->format($company);
            }
        }

        return $results;
    }

    private function extractPayload(array $payload, bool $isCreate): array
    {
        $fields = [
            'name',
            'code',
            'contact_phone',
            'contact_email',
            'website',
            'tracking_url',
            'delivery_type',
            'support_cod',
            'api_type',
            'api_config',
            'required_fields',
            'status',
            'sort',
        ];

        $data = array_intersect_key($payload, array_flip($fields));

        if ($isCreate) {
            if (empty($data['name'])) {
                throw new InvalidArgumentException('物流公司名称不能为空');
            }
            if (empty($data['code'])) {
                throw new InvalidArgumentException('物流公司编码不能为空');
            }
        }

        if (array_key_exists('support_cod', $data)) {
            $data['support_cod'] = (int) $data['support_cod'];
        }

        if (array_key_exists('status', $data)) {
            $data['status'] = (int) $data['status'];
        }

        if (array_key_exists('sort', $data)) {
            $data['sort'] = (int) $data['sort'];
        }

        foreach (['api_config', 'required_fields'] as $jsonField) {
            if (array_key_exists($jsonField, $data) && !is_array($data[$jsonField])) {
                throw new InvalidArgumentException($jsonField . ' 必须是数组');
            }
        }

        return $data;
    }

    private function assertCodeUnique(string $code, ?int $excludeId = null): void
    {
        if ($code === '') {
            return;
        }

        $query = ShipmentCompany::where('code', $code);
        if ($excludeId !== null) {
            $query->where('id', '<>', $excludeId);
        }

        if ($query->count() > 0) {
            throw new InvalidArgumentException('物流公司编码已存在');
        }
    }
}
